package contegor.scire.core.modifications;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import contegor.scire.core.AreLinksAimingTargetId;
import contegor.scire.core.Connectable;
import contegor.scire.core.Container;
import contegor.scire.core.Id;
import contegor.scire.core.IdFragment;
import contegor.scire.core.InfiniteSingletonList;
import contegor.scire.core.MissingElementException;
import contegor.scire.core.Modifiable;
import contegor.scire.core.NonDeletableElementException;
import contegor.scire.core.ReturnPoint;
import contegor.scire.core.ReturnPointAdapter;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;
import contegor.scire.core.TransactionException;
import contegor.scire.core.Version;
import contegor.scire.core.WithConsequences;


/**
 * {@link Modification} used to notify a {@link Modifiable} that it must delete one of its elements.
 *
 * @version $Revision$
 * @author  Héctor Cirbián Pousa ($Author$)
 */
@Version("$Id$")
public class DeleteConnectable extends Modification<Connectable> implements WithConsequences<Connectable, Id> {
  private static final long serialVersionUID = 8630954785155917087L;

  private IdFragment     startPoint;
  private Id[]           nextPoints;
  private List<Task<Id>> consequences = null;

  public DeleteConnectable(Id targetContainerId, IdFragment startPoint, Id ... nextPoints) {
    super(targetContainerId);
    this.startPoint = startPoint;
    this.nextPoints = nextPoints;
  }

  @Override
  public int getDepth() {
    return 1 + (this.nextPoints != null ? this.nextPoints.length : 0);
  }

  public void process(Container container) throws NonDeletableElementException, TransactionException, MissingElementException {
    super.isValidForThisContainer(container);
    Id      deletedConceptId = null;
    Set<Id> withErasedLinks  = null;
    if ((nextPoints == null) || (nextPoints.length == 0)) {
      // We are going to delete an Identifiable, so we will need additional checks.
      withErasedLinks  = new TreeSet<Id>();
      deletedConceptId = new Id(targetContainerId, this.startPoint);
    }
    ((Modifiable)container).deleteElement(this.taskId.getTransactionId(), withErasedLinks, startPoint, nextPoints);
    if ((withErasedLinks != null) && (withErasedLinks.size() > 0)) {
      this.consequences = new ArrayList<Task<Id>>(withErasedLinks.size());
      TaskId subTaskId = this.taskId;
      for (Id id: withErasedLinks)
        this.consequences.add(new AreLinksAimingTargetId(subTaskId = subTaskId.next(subTaskId == this.taskId), id, deletedConceptId));
    }
    else
      this.consequences = Collections.emptyList();
    /*
    List<Id> affectedIds = 
    if (affectedIds != null) {
      this.consequences = new ArrayList<Task<Void>>(affectedIds.size());
      TaskId subTaskId = this.taskId;
      for (Id id: affectedIds)
        this.consequences.add(new UntrackExternalLink(subTaskId = subTaskId.next(subTaskId == this.taskId), id.getParentId(), id.getLastIdFragment(), container.getId()));
    }
    else
      this.consequences = Collections.emptyList();
    */

    // this.returnPoint.manage(this.taskId, this.taskId);
  }

  @Override
  public List<? extends Task<Id>> getConsequences() {
    return this.consequences;
  }

  @Override
  public List<? extends ReturnPoint<Id>> getConsequencesReturnPoints() {
    if (this.consequences == null)
      return Collections.emptyList();
    return new InfiniteSingletonList<ReturnPoint<Id>>(new ReturnPointAdapter<Id>() {
      public void manage(TaskId taskId, Id result) {
        returnPoint.manage(DeleteConnectable.this.taskId, new NonDeletableElementException("There are Links pointing to " + targetContainerId + "." + startPoint + (nextPoints != null?Arrays.toString(nextPoints):"") + " at " + result));
      };
      public void manage(TaskId taskId, Exception exception) {
        returnPoint.manage(DeleteConnectable.this.taskId, exception);
      }
    }, this.consequences.size());
  }
}

